/** File name: cellNetwork.cu 
*** Author: Tran Van Hoang
*** Version: 1.0
**/

#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cuda.h>
#include "lib/cellNetwork.h"
#include <Xlib.h>
#include "data.cu"
#include "network.cu"
#include "view.h"

#define WINDOW_TITLE "Cellular Automata with CUDA"

unsigned long  NUMBER_CYCLE = 10;
unsigned long  NUMBER_POLLUTED_NODE = 3;

const int nbCells = sizeof(Cells)/sizeof(CellArray);
//If the number of cells between network and data are different, chosing the smaller one 
int dataSize = ((nbCells > NODE_NUMBER) ? NODE_NUMBER : nbCells);

int main(int argc, char **argv)
{	
	curandState *devState_d;
	clock_t start, finish;

	dim3 tpb(NODE_NUMBER,1,1);

        cudaMalloc((curandState**)&devState_d, NODE_NUMBER * sizeof(curandState));

	if (argc == 1)
	{
	    printf("\n-- Default number cycle: %ld\n", NUMBER_CYCLE);
	    printf("\n-- Default number polluted: %ld\n", NUMBER_POLLUTED_NODE);
	}else if (argc == 2)
	{
	    printf("\n-- Number cycle: %s\n", argv[1]);
	    NUMBER_CYCLE = atol(argv[1]);
	    printf("\n-- Default number polluted: %ld\n", NUMBER_POLLUTED_NODE);
	}else//argc=3
	{
	    printf("\n-- Number cycle: %s\n", argv[1]);
	    NUMBER_CYCLE = atol(argv[1]);
	    NUMBER_POLLUTED_NODE = atol(argv[2]) * NODE_NUMBER / 100;
	    printf("\n-- Number polluted node: %ld\n", NUMBER_POLLUTED_NODE);
	}
	// setup seeds for random number generator
	printf("\n-- Number of nodes: %d\n", NODE_NUMBER);
    	setup_kernel <<< 1, tpb >>> ( devState_d, time(NULL) );

	//Launching main simulation
	start = clock();
	simulatingRiver(devState_d);
	finish = clock();
	printf("\n-- Time for execution (seconds): %.5f\n", ((double)(finish - start))/CLOCKS_PER_SEC);
	cudaDeviceReset();
	cudaFree(devState_d);
	return 0;
}

void simulatingRiver_h()
{
	int nodeSize, nodeStateSize;
	int *now_h, *next_h;
	NodeState *nowState_h, *nextState_h;
	clock_t start, finish;

	nodeSize = NODE_NUMBER * sizeof(int);
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	/***********/
	//Allocating memory to host variables
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Initilizing state of the nodes

	initState(nowState_h, NODE_NUMBER);

	NodeState *temp;	
	
	start = clock();
	for (int i = 0; i < NUMBER_CYCLE; i++)
	{
	    stepState_h(nowState_h, nextState_h, channels_h, NODE_NUMBER);
	    //Preparing for the next step	
	    temp = nextState_h;
	    nextState_h = nowState_h;
	    nowState_h = temp;	    	    	    	    
	}
	
	finish = clock();
	printf("\n-- Time for execution (seconds): %.5f\n", ((double)(finish - start))/CLOCKS_PER_SEC);

	free(now_h);
	free(next_h);
	free(nowState_h);
	free(nextState_h);
}

void simulatingRiverGUI_h()
{
	int plotSize, NSize, nodeSize, nodeStateSize;
	int *now_h, *next_h;
	NodeState *nowState_h, *nextState_h;
	Colormap colormap;

	plotSize = 1;
	NSize = 256;
	nodeSize = NODE_NUMBER * sizeof(int);
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	Display *dsp = XOpenDisplay(NULL);	
	if (!dsp){ return;}
	colormap = DefaultColormap(dsp, 0);

	int screenNumber = DefaultScreen(dsp);
	unsigned long white = WhitePixel(dsp, screenNumber);
	unsigned long black = BlackPixel(dsp, screenNumber);	

	Window win = XCreateSimpleWindow(dsp,
				DefaultRootWindow(dsp),
				50, 50,// origin
				NSize*plotSize*3, NSize*plotSize*3,// size
				0, black,//border
				white//background
	);

	XStoreName(dsp, win, WINDOW_TITLE);
	XMapWindow(dsp, win);
	
	GC gc = XCreateGC(dsp, win, 0, NULL);	
	
	long eventMask = StructureNotifyMask;
	XSelectInput(dsp, win, eventMask);
	XEvent evt;
	do
	{
		XNextEvent(dsp, &evt);
	}while(evt.type != MapNotify);	

	/***********/
	//Allocating memory to host variables
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Initilizing state of the nodes

	initState(nowState_h, NODE_NUMBER);

	printf("\n-- Initilizating image...\n");

	int density;

	for (int j = 0; j < NODE_NUMBER; j++)
	{
	     density = (int) (nowState_h[j].density * 100.0);
	     showCellDensity(Cells[j], dsp, colormap, win, gc, plotSize, density);
	}


	printf("\n-- Click on the window to continue!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	NodeState *temp;	
	
	for (int i = 0; i < NUMBER_CYCLE; i++)
	{
	    stepState_h(nowState_h, nextState_h, channels_h, NODE_NUMBER);

	    printf("=> Step %d computed!\n  Refreshing view ...\n", i + 1);
	
	    for (int j = 0; j < NODE_NUMBER; j++)
	    {
		density = (int) (nextState_h[j].density * 100.0);
	    	showCellDensity(Cells[j], dsp, colormap, win, gc, plotSize, density);
	    }

	    //Preparing for the next step	
	    temp = nextState_h;
	    nextState_h = nowState_h;
	    nowState_h = temp;	    	    	    	    
	}
	
	free(now_h);
	free(next_h);
	free(nowState_h);
	free(nextState_h);
}

void simulatingRiver(curandState *devState_d)
{
	int nodeSize, blockSize, channelSize, nodeStateSize;
	int *now_h, *next_h, *now_d, *next_d;
	canaux *channels_d;	
	NodeState *nowState_h, *nowState_d, *nextState_h, *nextState_d, *buffState_d;

	blockSize = 512;
	nodeSize = NODE_NUMBER * sizeof(int);
	channelSize = NODE_NUMBER * sizeof(canaux);	
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	/***********/
	//Allocating memory to host variables
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Allocating memory to device variable
	cudaMalloc((void**)&now_d, nodeSize);
	cudaMalloc((void**)&next_d, nodeSize);
	cudaMalloc((canaux**)&channels_d, channelSize);
	cudaMalloc((NodeState**)&nowState_d, nodeStateSize);
	cudaMalloc((NodeState**)&nextState_d, nodeStateSize);
	cudaMalloc((NodeState**)&buffState_d, nodeStateSize);

	//Initilizing state of the nodes

	initState(nowState_h, NODE_NUMBER);

	cudaMemcpy(channels_d, channels_h, channelSize, cudaMemcpyHostToDevice);
	
	NodeState *temp;	
	
	for (int i = 0; i < NUMBER_CYCLE; i++)
	{
	    cudaMemcpy(nowState_d, nowState_h, nodeStateSize, cudaMemcpyHostToDevice);

	    stepState<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(nowState_d, nextState_d, channels_d, NODE_NUMBER, devState_d);

	    cudaMemcpy(nextState_h, nextState_d, nodeStateSize, cudaMemcpyDeviceToHost);

	    //Preparing for the next step	
	    temp = nextState_d;
	    nextState_d = nowState_d;
	    nowState_d = temp;	    	    	    	    
	}
	
	cudaFree(now_d);
	cudaFree(next_d);
	cudaFree(channels_d);
	cudaFree(nowState_d);
	cudaFree(nextState_d);
	cudaFree(buffState_d);

	free(now_h);
	free(next_h);
	free(nowState_h);
	free(nextState_h);
}

void simulatingRiverGUI(curandState *devState_d)
{
	int plotSize, NSize, blockSize, nodeSize, channelSize, nodeStateSize;
	int *now_h, *next_h, *now_d, *next_d;
	canaux *channels_d;
	Colormap colormap;
	NodeState *nowState_h, *nowState_d, *nextState_h, *nextState_d, *buffState_d;

	plotSize = 1;
	NSize = 256;
	blockSize = 512;
	nodeSize = NODE_NUMBER * sizeof(int);
	channelSize = NODE_NUMBER * sizeof(canaux);	
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	Display *dsp = XOpenDisplay(NULL);	
	if (!dsp){ return;}
	colormap = DefaultColormap(dsp, 0);

	int screenNumber = DefaultScreen(dsp);
	unsigned long white = WhitePixel(dsp, screenNumber);
	unsigned long black = BlackPixel(dsp, screenNumber);	

	Window win = XCreateSimpleWindow(dsp,
				DefaultRootWindow(dsp),
				50, 50,// origin
				NSize*plotSize*3, NSize*plotSize*3,// size
				0, black,//border
				white//background
	);

	XStoreName(dsp, win, WINDOW_TITLE);
	XMapWindow(dsp, win);
	
	GC gc = XCreateGC(dsp, win, 0, NULL);	
	
	long eventMask = StructureNotifyMask;
	XSelectInput(dsp, win, eventMask);
	XEvent evt;
	do
	{
		XNextEvent(dsp, &evt);
	}while(evt.type != MapNotify);	
		
	/***********/
	//Allocating memory to host variables
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Allocating memory to device variable
	cudaMalloc((void**)&now_d, nodeSize);
	cudaMalloc((void**)&next_d, nodeSize);
	cudaMalloc((canaux**)&channels_d, channelSize);
	cudaMalloc((NodeState**)&nowState_d, nodeStateSize);
	cudaMalloc((NodeState**)&nextState_d, nodeStateSize);
	cudaMalloc((NodeState**)&buffState_d, nodeStateSize);
	//Initilizing state of the nodes
	///// New version: Pollution diffusion

	initState(nowState_h, NODE_NUMBER);
	cudaMemcpy(nowState_d, nowState_h, nodeStateSize, cudaMemcpyHostToDevice);
	cudaMemcpy(channels_d, channels_h, channelSize, cudaMemcpyHostToDevice);

	//int arrState[NODE_NUMBER];
	
	printf("\n-- Initilizating image...\n");
	int density;
	for (int j = 0; j < NODE_NUMBER; j++)
	{
	     density = (int) (nowState_h[j].density * 100.0);
	     showCellDensity(Cells[j], dsp, colormap, win, gc, plotSize, density);
	}

	for (int j = 0; j < NODE_NUMBER; j++)
	{
	     density = (int) (nowState_h[j].density * 100.0);
	     if (density > 0.0)
	     	showCellDensity(Cells[j], dsp, colormap, win, gc, plotSize, density);
	}

	printf("\n-- Click on the window to continue!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);
	
	NodeState *temp;

	printf("-- Simulation is running: %ld cycles ...\n", NUMBER_CYCLE);

	for (int i = 0; i < NUMBER_CYCLE; i++)
	{
	    stepState<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(nowState_d, nextState_d, channels_d, NODE_NUMBER, devState_d);

	    cudaMemcpy(nextState_h, nextState_d, nodeStateSize, cudaMemcpyDeviceToHost);

	    printf("=> Step %d computed!\n  Refreshing view ...\n", i + 1);
	
	    for (int j = 0; j < NODE_NUMBER; j++)
	    {
		density = (int) (nextState_h[j].density * 100.0);
		if (density > 0.0)
	    	    showCellDensity(Cells[j], dsp, colormap, win, gc, plotSize, density);
	    }
	    //Preparing for the next step	
	    temp = nextState_d;
	    nextState_d = nowState_d;
	    nowState_d = temp;	    	    	    	    
	}
	
	printf("\nClick on window to exit!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);
	

	//Releasing resources
	XDestroyWindow(dsp, win);
	XCloseDisplay(dsp);
		
	cudaFree(now_d);
	cudaFree(next_d);
	cudaFree(channels_d);
	cudaFree(nowState_d);
	cudaFree(nextState_d);
	cudaFree(buffState_d);

	free(now_h);
	free(next_h);
	free(nowState_h);
	free(nextState_h);
	//return 0;
}
